#!-*-coding: utf-8 -*-
"""
21点游戏
"""
import random, time
from threading import Timer


class ErrorInvalidCharacter(Exception):
    """
    desc:   用户输入的答案包含非法字符或非给定字符的异常
    """
    pass


class ErrorInvalidExpression(Exception):
    """
    desc:   用户输入的答案不是合法的表达式的异常
    """
    pass


class ErrorNotInGameTime(Exception):
    """
    desc:   不在游戏时间参与游戏的异常
    """
    pass


_INVALID_ANSWER = -100000   # 无效的数学式结果

class Winner:
    """
    desc:   游戏获胜者信息, 包含用户名和答案
    """
    def __init__(self):
        self.name = None
        self.answer = _INVALID_ANSWER

    def init(self):
        self.__init__()


class Game21:
    def __init__(self):
        self.__expression_num_count = 4
        self.__expect_answer = 21
        # 合法的游戏字符
        self.__valid_op_character = ['+', '-', '*', '/', '(', ')', ' ']
        self.__valid_num_character = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
        self.__cur_given_num_character = [] # 当前给定的数字
        # 游戏是否正在运行
        self.__is_running = False
        self.__start_time = 0    # 游戏开始时间
        self.__end_time = 0     # 游戏结束时间
        self.__last_time = 180   # 游戏持续时间
        self.__winner = Winner()

    def __del__(self):
        self.__stop()

    def __check_valid_character(self, s):
        """
        desc:   检查用户输入的数学式子是否包含非法的字符
        param:
            s:  用户输入的数学式子
        return:
            bool:   数字式子的字符是否都合法
        """
        # 复制这轮21点游戏的所有数字字符
        given_num_character = self.__cur_given_num_character[:]
        for ch in s:
            if (ch not in self.__valid_op_character) and (ch not in self.__cur_given_num_character):
                return False
            # 将用过的数字剔除, 用于判断是否重复使用数字
            if ch in given_num_character:
                given_num_character.remove(ch)
        # 是否使用了所有数字
        return len(given_num_character) == 0

    @staticmethod
    def __check_valid_expression(s):
        """
        desc:   检查用户输入的数学式子是否是合法的
        param:
            s:  用户输入的数学式子
        return:
            bool:   数学式子是否都合法
        """
        try:
            eval(s)
        except:
            return False
        return True

    def add_player(self, player_name, answer):
        """
        desc:   参与此轮游戏的玩家
        param:
            player_name:  参与此轮游戏的玩家名
            answer: 玩家提交的数学式
        return: 空
        exception:  ErrorInvalidCharacter, ErrorInvalidExpression, ErrorNotInGameTime
        """
        if not self.is_running():
            raise ErrorNotInGameTime("当前不是游戏时间!")
        value = self.__eval_value(answer)
        # 答案大于21, 将其值置为无效的数学式结果
        if value > self.__expect_answer:
            value = _INVALID_ANSWER
        if value > self.__winner.answer:
            self.__winner.name = player_name
            self.__winner.answer = value

    def __eval_value(self, s):
        """
        desc:   获取数学式的结果
        param:
            s:  待计算的数学式
        return: 空
        exception:  ErrorInvalidCharacter, ErrorInvalidExpression
        """
        if not self.__check_valid_character(s):
            raise ErrorInvalidCharacter("输入的字符无效或非给定的字符组合!")
        if not self.__check_valid_expression(s):
            raise ErrorInvalidExpression("输入的表达式非法!")
        res = eval(s)   # 使用eval函数获取数学式结果, 因为做了字符检查, 因此不会被恶意使用
        return res

    def __generate_random_num(self):
        """
        desc:   产生此轮游戏的随机数字
        param:  空
        return: 空
        """
        self.__cur_given_num_character = []
        random.seed()   # 初始化随机种子
        tmp = random.sample("0123456789", self.__expression_num_count)  # 产生预置数量的随机数字
        self.__cur_given_num_character = tmp

    def get_given_num(self):
        """
        desc:   获取此轮游戏的随机数字
        param:  空
        return:
            list:   获取此轮游戏的随机数字
        """
        res = []
        for ch in self.__cur_given_num_character:
            res.append(int(ch))
        return res

    def get_last_time(self):
        """
        desc:   获取游戏的持续时间
        param:  空
        return:
            int:   游戏的持续时间
        """
        return self.__last_time

    def get_remain_time(self):
        """
        desc:   获取此轮游戏的剩余时间, 返回负数表示游戏已结束
        param:  空
        return:
            int:   游戏的剩余时间
        """
        return self.__end_time - time.time()

    def is_running(self):
        """
        desc:   获取此轮游戏是否正在进行
        param:  空
        return:
            bool:   此轮游戏是否正在进行
        """
        return self.__is_running

    def start(self):
        """
        desc:   开始此轮游戏
        param:  空
        return: 空
        """
        self.__generate_random_num()    #   产生随机数字
        self.__is_running = True
        self.__start_time = time.time()
        self.__end_time = self.__start_time + self.__last_time
        self.__winner.init()
        stop_timer = Timer(self.__last_time, self.__stop,)  # 启动游戏停止计时器
        stop_timer.start()

    def __stop(self):
        """
        desc:   停止此轮游戏
        param:  空
        return: 空
        """
        self.__is_running = False
        self.__start_time = 0
        self.__end_time = 0

    def get_winner_name(self):
        """
        desc:   获取此轮游戏胜利者用户名
        param:  空
        return:
            str:    此轮游戏胜利者用户名, None表示此轮没有答案或没有合法的答案
        """
        return self.__winner.name


if __name__ == "__main__":
    t = Game21()
    t.start()
    expression = t.get_given_num()
    print(expression)
    count = 1
    while t.is_running():
        inExp = str(raw_input())
        name = str(count)
        count += 1
        try:
            t.add_player(name, inExp)
        except ErrorNotInGameTime:
            print("当前不是游戏时间!")
        except ErrorInvalidCharacter:
            print("输入的字符无效或非给定的字符组合!")
        except ErrorInvalidExpression:
            print("输入的表达式非法!")
        else:
            print("成功参加游戏," + str(int(t.get_remain_time())) + "秒后系统会通知赢家!")
    if t.get_winner_name() == None:
        print("这轮21点游戏没有玩家参与!")
    else:
        print("这轮21点游戏的赢家是:" + t.get_winner_name())

